// pkg/registry/puller.go
// 镜像拉取器

package registry

import (
	"context"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
)

// Puller 镜像拉取器
type Puller struct {
	client   *Client
	cacheDir string
}

// NewPuller 创建拉取器
func NewPuller(cacheDir string) *Puller {
	return &Puller{
		client:   NewClient(),
		cacheDir: cacheDir,
	}
}

// PullImage 拉取镜像
func (p *Puller) PullImage(ctx context.Context, imageRef string, lazy bool) ([]string, error) {
	// 解析镜像引用
	ref, err := ParseImageRef(imageRef)
	if err != nil {
		return nil, fmt.Errorf("invalid image reference: %w", err)
	}

	// 拉取清单
	manifest, err := p.client.PullManifest(ctx, ref)
	if err != nil {
		return nil, fmt.Errorf("failed to pull manifest: %w", err)
	}

	layerPaths := make([]string, 0)

	// 拉取所有层
	for i, layer := range manifest.Layers {
		layerPath, err := p.pullLayer(ctx, ref, &layer, i, lazy)
		if err != nil {
			log.Printf("Warning: failed to pull layer %s: %v", layer.Digest, err)
			// 继续尝试拉取其他层
			continue
		}
		layerPaths = append(layerPaths, layerPath)
	}

	return layerPaths, nil
}

// pullLayer 拉取单个层
func (p *Puller) pullLayer(ctx context.Context, ref *ImageRef, layer *LayerDescriptor, index int, lazy bool) (string, error) {
	layerDir := filepath.Join(p.cacheDir, fmt.Sprintf("layer-%d", index))
	os.MkdirAll(layerDir, 0o755)

	layerPath := filepath.Join(layerDir, "data.tar.gz")

	// 如果已存在，直接返回
	if _, err := os.Stat(layerPath); err == nil {
		return layerPath, nil
	}

	if lazy {
		// 懒加载模式：仅下载元数据/索引
		return p.pullLayerLazy(ctx, ref, layer, layerPath)
	} else {
		// 全量拉取
		return p.pullLayerFull(ctx, ref, layer, layerPath)
	}
}

// pullLayerFull 全量拉取层
func (p *Puller) pullLayerFull(ctx context.Context, ref *ImageRef, layer *LayerDescriptor, layerPath string) (string, error) {
	reader, err := p.client.PullLayer(ctx, ref, layer.Digest)
	if err != nil {
		return "", fmt.Errorf("failed to pull layer: %w", err)
	}
	defer reader.Close()

	file, err := os.Create(layerPath)
	if err != nil {
		return "", fmt.Errorf("failed to create layer file: %w", err)
	}
	defer file.Close()

	if _, err := io.Copy(file, reader); err != nil {
		return "", fmt.Errorf("failed to write layer: %w", err)
	}

	log.Printf("Downloaded layer %s (%d bytes)", layer.Digest, layer.Size)
	return layerPath, nil
}

// pullLayerLazy 懒加载拉取层
func (p *Puller) pullLayerLazy(ctx context.Context, ref *ImageRef, layer *LayerDescriptor, layerPath string) (string, error) {
	// 仅创建元数据文件，延迟实际数据下载
	metaPath := layerPath + ".meta"

	file, err := os.Create(metaPath)
	if err != nil {
		return "", fmt.Errorf("failed to create metadata file: %w", err)
	}
	defer file.Close()

	// 记录层信息用于后续按需下载
	fmt.Fprintf(file, "digest=%s\nsize=%d\n", layer.Digest, layer.Size)

	log.Printf("Created lazy layer metadata for %s", layer.Digest)
	return layerPath, nil
}
